Wait until all jQuery Ajax requests are done?
Asked Answered
D

22

762

How do I make a function wait until all jQuery Ajax requests are done inside another function?

In short, I need to wait for all Ajax requests to be done before I execute the next. But how?

Dobbins answered 14/9, 2010 at 14:2 Comment(1)
What do you mean by "done" ? I understand it as "all requests have finished either successfully or not" (resolved or rejected). But you may mean "all requests have finished successfully" (resolved). see all the variations in api.jquery.com/category/deferred-objectInflammable
C
1026

jQuery now defines a when function for this purpose.

It accepts any number of Deferred objects as arguments, and executes a function when all of them resolve.

That means, if you want to initiate (for example) four ajax requests, then perform an action when they are done, you could do something like this:

$.when(ajax1(), ajax2(), ajax3(), ajax4()).done(function(a1, a2, a3, a4){
    // the code here will be executed when all four ajax requests resolve.
    // a1, a2, a3 and a4 are lists of length 3 containing the response text,
    // status, and jqXHR object for each of the four ajax calls respectively.
});

function ajax1() {
    // NOTE:  This function must return the value 
    //        from calling the $.ajax() method.
    return $.ajax({
        url: "someUrl",
        dataType: "json",
        data:  yourJsonData,            
        ...
    });
}

In my opinion, it makes for a clean and clear syntax, and avoids involving any global variables such as ajaxStart and ajaxStop, which could have unwanted side effects as your page develops.

If you don't know in advance how many ajax arguments you need to wait for (i.e. you want to use a variable number of arguments), it can still be done but is just a little bit trickier. See Pass in an array of Deferreds to $.when() (and maybe jQuery .when troubleshooting with variable number of arguments).

If you need deeper control over the failure modes of the ajax scripts etc., you can save the object returned by .when() - it's a jQuery Promise object encompassing all of the original ajax queries. You can call .then() or .fail() on it to add detailed success/failure handlers.

Chauvinism answered 25/3, 2012 at 23:54 Comment(11)
This should be marked as a correct answer because it's simple, efficient and works great. Also, it should be noted that $.when returns a Promise object which has more useful methods, not only .done. For example, with .then(onSuccess, onFailure) method you could react when both requests succeed or at least one of them fails.Claudetta
Is it possible to bunch the requests ajax1..4 into an array and pass that?Participation
@Participation See linked question... it's a little complicated but can be doneChauvinism
Be careful with the fail case. Unlike done, fail fires immediately on the first fail and disregards the remaining deferreds.Balderas
@Claudetta thanks for highlighting the fact that an onFailure function could be attached. As I pointed out in a comment to the OP's question: he might want to indicate more precisely what he meant by "done". "Ryan Mohr" did also have a very good point regarding the fact that fail behaves differently as done, some further reading to be done about Promises I guess html5rocks.com/en/tutorials/es6/promisesInflammable
It is great to give people exposure to the when method, and to promises in general, but I think this isn't the best answer. If any of those ajax functions anywhere down the line create another ajax request, and then don't integrate that new promise into the chain correctly... those requests will escape this technique. For example, I can't use this technique without modifying the Shopify library I'm using for ajax add-to-cart behaviour, because it wasn't written in a 'promisy' way, and never returns the xhr objects it creates. Does this make sense? Still a great answer, though!Footpoundsecond
(I think it bears mentioning that while I understand the OP's question is about dealing with ajax calls from within a single function... I believe interpreting this as meaning "code that calls not library functions" would be too generous.)Footpoundsecond
i tryed it and in my console log it do two ajax call. i mean, it double all ajax callCai
Does not work when requests fail. Downvote. Will vote up, when answer is complete. This means, I want to continue only when all requests, including failed are completed.Burkey
Great stuff, I have been stuck on this for a while but I had missed the information where you have to return the ajax within the function, thank you for a great explanationAffairs
@RyanMohr use Promise.allSettled([$.ajax(), $.ajax()]).then() to wait until all are resolvedWeldon
L
334

If you want to know when all ajax requests are finished in your document, no matter how many of them exist, use $.ajaxStop event this way:

$(document).ajaxStop(function () {
  // 0 === $.active
  $(this).unbind('ajaxStop'); // to stop this event repeating further
});

In this case, neither you need to guess how many requests are happening in the application that might finish in the future, nor dig into functions' complex logic or find which functions are doing HTTP(S) requests.

$.ajaxStop here can also be bound to any HTML node that you think it might be modified by request.


Update:
If you want to stick with ES syntax, then you can use Promise.all for known ajax methods:

Promise.all([ajax1(), ajax2()]).then(() => {
  // all requests finished successfully
}).catch(() => {
  // all requests finished, but one or more failed
})

Interestingly, it works with Promises as well as $.ajax requests.

Here is the jsFiddle demonstration.


Update 2:
Yet more recent version using async/await syntax:

try {
  const results = await Promise.all([ajax1(), ajax2()])
  // do other actions
} catch(ex) { }
Lexicostatistics answered 26/12, 2012 at 8:42 Comment(11)
+1 Much better than other answers in case you have to deal with 3rd party scripts with anonymous callbacks/closures.S
@S Valid point but it's not what the question was asking. It's not very good if you don't want to wait for all AJAX calls to return. The question is specific about waiting for the AJAX calls you've made on your own (called inside another function, as the OP wrote). Some other code may have made another AJAX call that you don't want to wait for.Sprinkling
Compared to the when() solution, it has the advantage to work even if the number of ajax calls is not known.Prowl
Compared to the when() solution, it has the large disadvantage not to work well together with other components, since it shares a document-wide global state. If there is some long polling going on continuously, it might even never fire.Herve
@ArsenKhachaturyan Assuming the original poster meant (either requests failed or successed by "done"): you might want to also use .ajaxError(), as otherwise I believe your code will not be excuted if your ajax calls fail. $(document).ajaxStop(function () { callYourFunction(); }); $(document).ajaxError(function () { callYourFunction(); }); see api.jquery.com/category/ajax/global-ajax-event-handlersInflammable
You're not correct @AdrienBe, ajaxStop handles all ajax requests no matter do they succeed or not, just as proof of my words look at this jsfiddle.net/36votxba/2Lexicostatistics
If you call another loop of AJAX requests using this you get an endless loop. Oops. :)Exosphere
Actually you are not supposed to call AJAX requests inside ajaxStop, because it's meaning is to let you know when AJAX's are done. Try do $('#somebutton').click(function() { $('#somebutton).click();}). What you think, will above create forever loop ?) If yes then think the same way for ajaxStop event.Lexicostatistics
This seems like the best solution except that it binds the event to the document and it fires then everytime all ajax finish. So if this is undesired, do unbind it in the callback using $(document).off('ajaxStop');Challenge
does the first example $(document).ajaxStop() work for xmlHttpRequest ajax method?Clitoris
Frankly never tried it on xmlHttpRequest, however, according to documentation jQuery can deal with such requests.Lexicostatistics
D
33

I found a good answer by gnarf my self which is exactly what I was looking for :)

jQuery ajaxQueue

//This handles the queues    
(function($) {

  var ajaxQueue = $({});

  $.ajaxQueue = function(ajaxOpts) {

    var oldComplete = ajaxOpts.complete;

    ajaxQueue.queue(function(next) {

      ajaxOpts.complete = function() {
        if (oldComplete) oldComplete.apply(this, arguments);

        next();
      };

      $.ajax(ajaxOpts);
    });
  };

})(jQuery);

Then you can add a ajax request to the queue like this:

$.ajaxQueue({
        url: 'page.php',
        data: {id: 1},
        type: 'POST',
        success: function(data) {
            $('#status').html(data);
        }
    });
Dobbins answered 14/9, 2010 at 15:26 Comment(1)
It looks like you've forgotten to give proper attribution to this answer, I've added it.Pedroza
D
22

NOTE: The above answers use functionality that didn't exist at the time that this answer was written. I recommend using jQuery.when() instead of these approaches, but I'm leaving the answer for historical purposes.

-

You could probably get by with a simple counting semaphore, although how you implement it would be dependent on your code. A simple example would be something like...

var semaphore  = 0,     // counting semaphore for ajax requests
    all_queued = false; // bool indicator to account for instances where the first request might finish before the second even starts

semaphore++;
$.get('ajax/test1.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test2.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test3.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test4.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

// now that all ajax requests are queued up, switch the bool to indicate it
all_queued = true;

If you wanted this to operate like {async: false} but you didn't want to lock the browser, you could accomplish the same thing with a jQuery queue.

var $queue = $("<div/>");
$queue.queue(function(){
    $.get('ajax/test1.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test2.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test3.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test4.html', function(data) {
        $queue.dequeue();
    });
});
Dulcet answered 14/9, 2010 at 14:26 Comment(9)
This seems like it would overly complicate a trivial problem.Bambi
It's really not all that complicated. Counting semaphores are a common mechanism in CS. If you prefer though, the example using jQuery queues would work as well without having to implement the semaphore yourself.Dulcet
Thanks @Dulcet for the answer - I wrote a utility function based on your answer. Refer: #3710097Loxodromics
I do not see a problem with the semaphore counter, however, I do see a problem with the idea of having FOUR functions to handle the resulting callback. You should define a function first, then reference that function in each .get(). That way at least you do not duplicate that code. Not only that but declaring a function(){} each time allocates memory each time! Rather bad practice if you could call a statically defined function.Dachy
@AlexisWilke This is a 4.5 year old answer, and it was meant to be an example of how semaphores and queues work. You're thinking a little too hard about this, and I don't think CAPITALIZATION TO MAKE A POINT is necessary.Dulcet
Well... I'm not the one who gave you a -1... and I understand that answers do tend to age. Yet, people keep finding them and as far as I know it is not forbidden to give info to people who will potentially make use of them still today.Dachy
why do we need var semaphore ? Isn't enough to have only all_queued ?Banter
I like your answer (although if you're using jQuery then you should use $.when()), but I'd use only the semaphore variable (making all_queued unnecesssary) by initializing semaphore with the number of AJAX requests (in your exmaple 4).Spavined
The answer is almost 6 years old. jQuery didn't have $.when() when this was written.Dulcet
O
21

Use the ajaxStop event.

For example, let's say you have a loading ... message while fetching 100 ajax requests and you want to hide that message once loaded.

From the jQuery doc:

$("#loading").ajaxStop(function() {
  $(this).hide();
});

Do note that it will wait for all ajax requests being done on that page.

Orlantha answered 24/5, 2011 at 11:42 Comment(4)
This assumes that you know there won't be any other AJAX requests on the page, not a very good assumptionSprinkling
As of jQuery 1.8, the .ajaxStop() method should only be attached to document.Abbe
Correct me if I'm wrong but won't this turn your project into an "old school web forms" site? I mean if you your entire page has to wait for a request before it can continue then what's the point of the ajax request in the first place?Lindahl
@Lindahl in our case, I'm looping through a jquery collection to build new stuff and need to know about the whole collection when it's done, before making some layout adjustments. Doesn't seem like a particularly unusual case. Would be bad if a bunch of other ajax stuff might be in process, but it won't be, here.Godevil
B
10

A little workaround is something like this:

// Define how many Ajax calls must be done
var ajaxCalls = 3;
var counter = 0;
var ajaxCallComplete = function() {
    counter++;
    if( counter >= ajaxCalls ) {
            // When all ajax calls has been done
        // Do something like hide waiting images, or any else function call
        $('*').css('cursor', 'auto');
    }
};

var loadPersons = function() {
        // Show waiting image, or something else
    $('*').css('cursor', 'wait');

    var url = global.ctx + '/loadPersons';
    $.getJSON(url, function(data) {
            // Fun things
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCountries = function() {
    // Do things
    var url = global.ctx + '/loadCountries';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCities = function() {
    // Do things
    var url = global.ctx + '/loadCities';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

$(document).ready(function(){
    loadPersons();
    loadCountries();
    loadCities();
});

Hope can be useful...

Blockage answered 14/9, 2010 at 14:2 Comment(1)
While the other answers are technically better since it's way easier to understand, I really like this one. Nice!Teacher
B
10

jQuery allows you to specify if you want the ajax request to be asynchronous or not. You can simply make the ajax requests synchronous and then the rest of the code won't execute until they return.

For example:

jQuery.ajax({ 
    async: false,
    //code
});
Bridal answered 14/9, 2010 at 14:27 Comment(33)
One thing to note is that using { async: false } can temporarily lock the browser. api.jquery.com/jQuery.ajaxDulcet
Exactly! This way you know that at the end of the function which calls all the ajax requests, that they are all finished executing.Bridal
BBonifield, I am experiencing this behaviour in IE 6. Quite annoying!Hogtie
This runs contrary to standard jQuery/Javascript practice. AJAX is always supposed to be asynchronous. You should use jQuery.when() instead.Binah
It's terribly bad idea! Never ever do that! Blocking = not responding to user actions at all, even to scrolling or anything! (Also, async: false is going to be deprecated in jQuery 1.8.)Claudetta
Particularly if the request fails or takes a long time for some unpredictable reason (which, by Murphy's Law, is bound to happen!), this is usually a bad idea for production code due to browser locking as stated above.Chauvinism
Unfortunetly they removed { async : false } from 1.8 which is really bad. Sometimes you just need blocking call with optional timeout.Haemolysin
AJAX means "Asynchronous Javascript and XML", it's not supposed to be synchronous. Doing synchronous AJAX requests is not only paradoxal, but a (very) bad practice.Cortex
This is a horribly bad idea. DO NOT USE THIS ANSWER.Bracketing
@Learner I wouldn't want to use your UI if you are willing to lock it up for a few seconds by making your AJAX calls synchronous. Please don't use it.Sprinkling
This is bad for gui usabilityAbbe
With HTML 5, from what I've read, some browser will drop (if not already done) the capability of sending a synchronous AJAX call. So such code will break.Dachy
@Nickolodeon, async option of jQuery is not deprecated. It is only its use combined with deferred jqXHR callbacks (done/fail/always) which is deprecated. Used with settings callbacks (success/error/complete), it still works synchronously.Lyceum
As stated by Alexis Wilke, async with value false is gone deprecated (by standards, not by jQuery) in most browser use cases. It may throw exceptions in newer version of browsers. See xhr.spec.whatwg.org/#sync-warning (applies to async parameter of xhr open method, which is what uses jQuery).Lyceum
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help xhr.spec.whatwg.orgEnsepulcher
this is the worse solution from UX perspective but solves the problem !Explanatory
As webmaster it's my decision whether or not to give my users a potentially bad experience. The community knows NOTHING about my website, so how are they to judge how my site works? Deprecating async: false is a slap in the face of anyone who can genuinely need it.Wendell
@Wendell Nobody genuinely needs it. There's always a better way to code whatever it is you're doing and make it work asynchronously.Sherborn
@Sherborn please visit chris.chrisjneeds.com/start.html and tell me how you would display the images AFTER they have completely loaded so that the user doesn't have to watch them streaming down the page like ancient scrolls being unravelled.Wendell
@Wendell Sorry I'm not seeing your point. In your code, the synchronous XHR is the HEAD request to test if the image exists (which IMO is a bit pointless, you could just try and load the image and wait for it to fail. You're using two HTTP requests where one would do.) But the way you are actually loading the images themselves does not use AJAX and is asynchronous anyway. If you watch your Network tab you'll see the the loading of the actual images still happens in parallel. If you wanted to avoid that (which I think is what you're trying to say), you haven't succeeded.Sherborn
@Wendell P.S. if you still feel the HEAD is necessary, you can call it asynchronously without a problem - you can simply avoid setting the img.src until the HEAD request returns successfully. Your scenario doesn't demonstrate any requirement at all for synchronous AJAX as far as I can see. If you did want to make your images load synchronously for some reason, you could wrap the image load event in a Promise and then chain a series of promises together. But that would just slow down the loading process. Loading them in parallel is a better UX.Sherborn
@Sherborn with due respect you're missing various points. a) i know my page doesn't work as intended, hence asking you to "fix" it, if you can. b) loading the images in parallel is NOT the requirement. what we'd like to see is no image displayed until ALL of them have loaded. c) the reason for testing if the img does not exist is so that another img can be found to replace it. btw my mail address is chis at chrisjneeds dot comWendell
@Wendell you would be better to open a proper question about this. Then I (and others) can spend time to help you fully. I didn't realise you were aware the page didn't work as intended, I was under the impression from your comment that you were trying to use that code to invalidate my point, as if it was an example of the necessity of synchronous AJAX. But now I understand better, I'm happy to help if you link me to a question here :-)Sherborn
@Sherborn i will do that when i have some time—but i really think my answer is here somewhere. i have tried many of the responses here and will try a few more before i set up a question. such a question will need to be carefully crafted as most readers will see it from an asynch POV and will be reluctant to help with creating synchronous code (i'm tired of being shouted at) :-)Wendell
@Wendell Actually here you go, for free, it was simpler than I thought, just using Promises: jsfiddle.net/32xgepjc/7 . The images are loaded in parallel, which saves time, but not displayed until all of them have finished successfully downloading. If an image fails to download, then it is replaced in the "pending" queue by a fresh request to a different URL. The HEAD request is removed (a direct request to an invalid URL will return a 404 anyway so the outcome is the same). The code is also far better encapsulated without all those horrible globals tightly coupling everything. Enjoy.Sherborn
@Wendell and as I said...no need for anything synchronous. It would just slow the whole thing down. More info on Promise.all is here.Sherborn
@Sherborn – thank you for the promise info, i will study it now. i never was able to get promise to work. if i and some friends have a website that's not intended for general consumption and we are prepared to wait, then "slow the whole thing down" is not anyone else's concern. ideally, we would like the next set of images to be loading into a hidden div whilst we are looking at the first set. but, since i can't get the first requirement to work, i haven't spent time on this yet...Wendell
@Wendell well that's exactly what my code would enable you to do (and without slowing anything down with sequential HTTP requests. Instead, the downloading of each set of images happens in parallel, but the actual displaying of the images is delayed until all of them have downloaded. Note also that there's no AJAX involved). Take a look at the demo link and you'll see.Sherborn
@Sherborn – i will look again, now. maybe i will understand your code better with the intervening two year's experience. i appreciate your feedback...Wendell
@Wendell No problem. If you have any further needs around this topic, e.g. extending the functionality or integrating it back into your website, then open a new question about it and ping me a comment with the link :-)Sherborn
@Sherborn – i missed your 32xgepjc fiddle comment. thank you in advance, will check it out now...Wendell
@Sherborn — i'm implementing a version using your solution but i'm bothered by "those horrible globals tightly coupling everything". i do this so i don't have to concern myself with scope. please explain the better way :-)Wendell
Let us continue this discussion in chat.Wendell
T
8

javascript is event-based, so you should never wait, rather set hooks/callbacks

You can probably just use the success/complete methods of jquery.ajax

Or you could use .ajaxComplete :

$('.log').ajaxComplete(function(e, xhr, settings) {
  if (settings.url == 'ajax/test.html') {
    $(this).text('Triggered ajaxComplete handler.');
    //and you can do whatever other processing here, including calling another function...
  }
});

though youy should post a pseudocode of how your(s) ajax request(s) is(are) called to be more precise...

Toplofty answered 14/9, 2010 at 15:7 Comment(0)
R
4

As other answers mentioned you can use ajaxStop() to wait until all ajax request are completed.

$(document).ajaxStop(function() {
     // This function will be triggered every time any ajax request is requested and completed
});

If you want do it for an specific ajax() request the best you can do is use complete() method inside the certain ajax request:

$.ajax({
    type: "POST",
    url: "someUrl",
    success: function(data) {
        // This function will be triggered when ajax returns a 200 status code (success)
    },
    complete: function() {
        // This function will be triggered always, when ajax request is completed, even it fails/returns other status code
    },
    error: function() {
        // This will be triggered when ajax request fail.
    }
});


But, If you need to wait only for a few and certain ajax request to be done? Use the wonderful javascript promises to wait until the these ajax you want to wait are done. I made a shortly, easy and readable example to show you how does promises works with ajax.
Please take a look to the next example. I used setTimeout to clarify the example.

// Note:
// resolve() is used to mark the promise as resolved
// reject() is used to mark the promise as rejected

$(document).ready(function() {
    $("button").on("click", function() {

        var ajax1 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://miro.medium.com/max/1200/0*UEtwA2ask7vQYW06.png",
                xhrFields: { responseType: 'blob'},
                success: function(data) {
                    setTimeout(function() {
                        $('#image1').attr("src", window.URL.createObjectURL(data));
                        resolve(" Promise ajax1 resolved");
                    }, 1000);
                },
                error: function() {
                    reject(" Promise ajax1 rejected");
                },
            });
        });

        var ajax2 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://cdn1.iconfinder.com/data/icons/social-media-vol-1-1/24/_github-512.png",
                xhrFields: { responseType: 'blob' },
                success: function(data) {
                    setTimeout(function() {
                         $('#image2').attr("src", window.URL.createObjectURL(data));
                         resolve(" Promise ajax2 resolved");
                    }, 1500);
                },
                error: function() {
                    reject(" Promise ajax2 rejected");
                },
            });
        });

        var ajax3 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://miro.medium.com/max/632/1*LUfpOf7teWvPdIPTBmYciA.png",
                xhrFields: { responseType: 'blob' },
                success: function(data) {
                    setTimeout(function() {
                         $('#image3').attr("src", window.URL.createObjectURL(data));
                         resolve(" Promise ajax3 resolved");
                    }, 2000);
                },
                error: function() {
                    reject(" Promise ajax3 rejected");
                },
            });
        });
        
        Promise.all([ajax1, ajax2, ajax3]).then(values => {
            console.log("We waited until ajax ended: " + values);
            console.log("My few ajax ended, lets do some things!!")
        }, reason => {
            console.log("Promises failed: " + reason);
        });
        
        // Or if you want wait for them individually do it like this
        // ajax1.then(values => {
        //    console.log("Promise 1 resolved: " + values)
        // }, reason => {
        //     console.log("Promise 1 failed: " + reason)
        // });
    });

});
img {
  max-width: 200px;
  max-height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Make AJAX request</button>
<div id="newContent">
    <img id="image1" src="">
    <img id="image2" src="">
    <img id="image3" src="">
</div>
Riha answered 15/1, 2019 at 9:13 Comment(0)
L
2

On the basis of @BBonifield answer, I wrote a utility function so that semaphore logic is not spread in all the ajax calls.

untilAjax is the utility function which invokes a callback function when all the ajaxCalls are completed.

ajaxObjs is a array of ajax setting objects [http://api.jquery.com/jQuery.ajax/].

fn is callback function

function untilAjax(ajaxObjs, fn) {
  if (!ajaxObjs || !fn) {
    return;
  }
  var ajaxCount = ajaxObjs.length,
    succ = null;

  for (var i = 0; i < ajaxObjs.length; i++) { //append logic to invoke callback function once all the ajax calls are completed, in success handler.
    succ = ajaxObjs[i]['success'];
    ajaxObjs[i]['success'] = function(data) { //modified success handler
      if (succ) {
        succ(data);
      }
      ajaxCount--;
      if (ajaxCount == 0) {
        fn(); //modify statement suitably if you want 'this' keyword to refer to another object
      }
    };
    $.ajax(ajaxObjs[i]); //make ajax call
    succ = null;
  };

Example: doSomething function uses untilAjax.

function doSomething() {
  // variable declarations
  untilAjax([{
    url: 'url2',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    }
  }, {
    url: 'url1',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    }
  }, {
    url: 'url2',
    dataType: 'json',
    success: function(response) {
      //do something with success data
    }
  }], function() {
    // logic after all the calls are completed.
  });
}
Loxodromics answered 19/2, 2012 at 15:33 Comment(0)
T
2

If you need something simple; once and done callback

        //multiple ajax calls above
        var callback = function () {
            if ($.active !== 0) {
                setTimeout(callback, '500');
                return;
            }
            //whatever you need to do here
            //...
        };
        callback();
Translation answered 21/5, 2013 at 14:3 Comment(2)
it can generate an endless loop !Sturgeon
This is an endless loop? When? When the AJAX never returns?Windywindzer
V
2

Also you could use async.js.

I think its better than $.when because you can merge all kinds of asynchronous call that does not support promises out of the box like timeouts, SqlLite calls etc. and not just ajax requests.

Voluble answered 4/2, 2016 at 18:32 Comment(0)
S
2

I highly recommend using $.when() if you're starting from scratch.

Even though this question has over million answers, I still didn't find anything useful for my case. Let's say you have to deal with an existing codebase, already making some ajax calls and don't want to introduce the complexity of promises and/or redo the whole thing.

We can easily take advantage of jQuery .data, .on and .trigger functions which have been a part of jQuery since forever.

Codepen

The good stuff about my solution is:

  • it's obvious what the callback exactly depends on

  • the function triggerNowOrOnLoaded doesn't care if the data has been already loaded or we're still waiting for it

  • it's super easy to plug it into an existing code

$(function() {

  // wait for posts to be loaded
  triggerNowOrOnLoaded("posts", function() {
    var $body = $("body");
    var posts = $body.data("posts");

    $body.append("<div>Posts: " + posts.length + "</div>");
  });


  // some ajax requests
  $.getJSON("https://jsonplaceholder.typicode.com/posts", function(data) {
    $("body").data("posts", data).trigger("posts");
  });

  // doesn't matter if the `triggerNowOrOnLoaded` is called after or before the actual requests 
  $.getJSON("https://jsonplaceholder.typicode.com/users", function(data) {
    $("body").data("users", data).trigger("users");
  });


  // wait for both types
  triggerNowOrOnLoaded(["posts", "users"], function() {
    var $body = $("body");
    var posts = $body.data("posts");
    var users = $body.data("users");

    $body.append("<div>Posts: " + posts.length + " and Users: " + users.length + "</div>");
  });

  // works even if everything has already loaded!
  setTimeout(function() {

    // triggers immediately since users have been already loaded
    triggerNowOrOnLoaded("users", function() {
      var $body = $("body");
      var users = $body.data("users");

      $body.append("<div>Delayed Users: " + users.length + "</div>");
    });

  }, 2000); // 2 seconds

});

// helper function
function triggerNowOrOnLoaded(types, callback) {
  types = $.isArray(types) ? types : [types];

  var $body = $("body");

  var waitForTypes = [];
  $.each(types, function(i, type) {

    if (typeof $body.data(type) === 'undefined') {
      waitForTypes.push(type);
    }
  });

  var isDataReady = waitForTypes.length === 0;
  if (isDataReady) {
    callback();
    return;
  }

  // wait for the last type and run this function again for the rest of the types
  var waitFor = waitForTypes.pop();
  $body.on(waitFor, function() {
    // remove event handler - we only want the stuff triggered once
    $body.off(waitFor);

    triggerNowOrOnLoaded(waitForTypes, callback);
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>Hi!</body>
Spelldown answered 28/8, 2017 at 11:15 Comment(0)
Z
2

I'm using size check when all ajax load completed

function get_ajax(link, data, callback) {
    $.ajax({
        url: link,
        type: "GET",
        data: data,
        dataType: "json",
        success: function (data, status, jqXHR) {
            callback(jqXHR.status, data)
        },
        error: function (jqXHR, status, err) {
            callback(jqXHR.status, jqXHR);
        },
        complete: function (jqXHR, status) {
        }
    })
}

function run_list_ajax(callback){
    var size=0;
    var max= 10;
    for (let index = 0; index < max; index++) {
        var link = 'http://api.jquery.com/ajaxStop/';
        var data={i:index}
        get_ajax(link,data,function(status, data){
            console.log(index)
            if(size>max-2){
                callback('done')
            }
            size++
            
        })
    }
}

run_list_ajax(function(info){
    console.log(info)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
Zeiler answered 5/11, 2018 at 17:59 Comment(1)
thumbs up to your example.Offenseless
H
2

To expand upon Alex's answer, I have an example with variable arguments and promises. I wanted to load images via ajax and display them on the page after they all loaded.

To do that, I used the following:

let urlCreator = window.URL || window.webkitURL;

// Helper function for making ajax requests
let fetch = function(url) {
    return $.ajax({
        type: "get",
        xhrFields: {
            responseType: "blob"
        },
        url: url,
    });
};

// Map the array of urls to an array of ajax requests
let urls = ["https://placekitten.com/200/250", "https://placekitten.com/300/250"];
let files = urls.map(url => fetch(url));

// Use the spread operator to wait for all requests
$.when(...files).then(function() {
    // If we have multiple urls, then loop through
    if(urls.length > 1) {
        // Create image urls and tags for each result
        Array.from(arguments).forEach(data => {
            let imageUrl = urlCreator.createObjectURL(data[0]);
            let img = `<img src=${imageUrl}>`;
            $("#image_container").append(img);
        });
    }
    else {
        // Create image source and tag for result
        let imageUrl = urlCreator.createObjectURL(arguments[0]);
        let img = `<img src=${imageUrl}>`;
        $("#image_container").append(img);
    }
});

Updated to work for either single or multiple urls: https://jsfiddle.net/euypj5w9/

Hatfield answered 15/11, 2018 at 22:18 Comment(0)
T
0

Look at my solution:

1.Insert this function (and variable) into your javascript file:

var runFunctionQueue_callback;

function runFunctionQueue(f, index, callback) {

  var next_index = index + 1

  if (callback !== undefined) runFunctionQueue_callback = callback;

  if (f[next_index] !== undefined) {
    console.log(index + ' Next function avalaible -> ' + next_index);
    $.ajax({
      type: 'GET',
      url: f[index].file,
      data: (f[index].data),
      complete: function() {
        runFunctionQueue(f, next_index);
      }
    });
  } else {
    console.log(index + ' Last function');
    $.ajax({
      type: 'GET',
      url: f[index].file,
      data: (f[index].data),
      async: false,
      complete: runFunctionQueue_callback
    });
  }
}

2.Buil an array with your requests, like this:

var f = [
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: 'file_path', data: {action: 'action', data: 'any_data}}
        ];

3.Create callback function:

function Function_callback() {
  alert('done');
}

4.Call the runFunctionQueue function with parameters:

runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
Tilth answered 2/5, 2011 at 12:30 Comment(0)
D
0

My solution is as follows

var request;
...
'services': {
  'GetAddressBookData': function() {
    //This is the primary service that loads all addressbook records 
    request = $.ajax({
      type: "POST",
      url: "Default.aspx/GetAddressBook",
      contentType: "application/json;",
      dataType: "json"
    });
  },

  ...

  'apps': {
    'AddressBook': {
      'data': "",
      'Start': function() {
          ...services.GetAddressBookData();
          request.done(function(response) {
            trace("ajax successful");
            ..apps.AddressBook.data = response['d'];
            ...apps.AddressBook.Filter();
          });
          request.fail(function(xhr, textStatus, errorThrown) {
            trace("ajax failed - " + errorThrown);
          });

Worked quite nicely. I've tried a lot of different ways of doing this, but I found this to be the simplest and most reusable. Hope it helps

Department answered 1/3, 2012 at 23:27 Comment(0)
U
0

I found simple way, it using shift()

function waitReq(id)
{
  jQuery.ajax(
  {
    type: 'POST',
    url: ajaxurl,
    data:
    {
      "page": id
    },
    success: function(resp)
    {
      ...........
      // check array length if not "0" continue to use next array value
      if(ids.length)
      {
        waitReq(ids.shift()); // 2
      )
    },
    error: function(resp)
    {
      ....................
      if(ids.length)
      {
        waitReq(ids.shift());
      )
    }
  });
}

var ids = [1, 2, 3, 4, 5];    
// shift() = delete first array value (then print)
waitReq(ids.shift()); // print 1
Ursel answered 12/1, 2016 at 15:25 Comment(0)
T
0

$.when doesn't work for me, callback(x) instead of return x worked as described here: https://mcmap.net/q/55460/-javascript-doesn-39-t-seem-to-wait-for-return-values

Temuco answered 5/10, 2018 at 10:48 Comment(0)
P
0

The below solution worked for me using $when

$.when(master.GetStateByName(stateName)).done(function(response) {
    if (response) {

    }
});

GetStateByName: function(stateName) {
    return $.ajax({
        type: 'POST',
        url: getStatesByName + '?stateName=' + stateName,
        async: false,
    });
}
Pasteup answered 17/8, 2021 at 13:24 Comment(0)
A
-3

Try this way. make a loop inside java script function to wait until the ajax call finished.

function getLabelById(id)
{
    var label = '';
    var done = false;
    $.ajax({
       cache: false,
       url: "YourMvcActionUrl",
       type: "GET",
       dataType: "json",
       async: false,
       error: function (result) {
         label='undefined';
         done = true;
        },
       success: function (result) {
            label = result.Message;
            done = true;
        }
     });

   //A loop to check done if ajax call is done.
   while (!done)
   {
      setTimeout(function(){ },500); // take a sleep.
   }

    return label;
}
Anagnorisis answered 27/2, 2014 at 23:32 Comment(3)
Your setTimeout() does NOT take a sleep. In this case, you just block all tabs until done becomes true.Dachy
I think that is this topic asking for: "Wait until all jQuery Ajax requests are done".Anagnorisis
Have you tested this code? my expectation is that done will never be true while the while loop is still running. If the while loop is running, the event loop can't continue and therefore will never run the callback to the ajax success.Steib
H
-3

This is working for me It's very simple

return $.ajax({
  type: 'POST',
  url: urlBaseUrl
  data: {someData:someData},
  dataType: "json",
  success: function(resultData) { 
  }
});
Henderson answered 30/9, 2020 at 5:20 Comment(1)
No this is not an answer, since he specifically asks for ALL requests. Your code is simple since its too simple, its only handling the response of a single request. Please read the question before answering.Souse

© 2022 - 2024 — McMap. All rights reserved.